"""
实习题：构造一个小（Mini）语言的词法分析程序。
设计一个包含简单算术表达式、赋值语句、IF语句的小语
言的文法。
根据此文法，构造一词法分析程序。输入以“#”为结束符
的源程序，输出为各类单词表和单词串文件。
要求：源程序和输出的单词串均以文件的形式存放。单词
的自身值均为其对应的表的指针，如标识符表的指针、常数表
的指针等。<class,value> & tables
词法错误类型：词法中未定义的字符及任何不符合词法单
元定义的字符。
"""
"""
输入：源程序以文件的形式，以#结尾，需要自己定义保留字，运算符，界限符集合
输出：以文件的形式，单词串：<class,value> 表:标识符表，数表，关键字表，运算符表，界限符表等
"""

inputPath="D:/VSCodeFiles/Python/lexicalAnalyse/res/input.c"
outputPath="D:/VSCodeFiles/Python/lexicalAnalyse/res/result.txt"

delimiter=['[', ']', ';', '.', '{', '}', '(', ')', ',']
Operations1=["=","<",">","!","+","-","*","/"]
Operations2=["|","&"]
Reserved={
    "int":1,
    "struct":2,
    "bool":3,
    "float":4,
    "if":5,
    "else":6,
    "do":7,
    "while":8,
    "return":9,
    "void":10,
    "true":11,
    "false":12,
    "string":13
}

def isReserved(s:str):#判断单词是否为保留字，若是则返回index
    if s in Reserved.keys():
        return s#是保留字
    return "id"#是标识符

def isNum(s:str):
    if len(s)==1:
        return True
    if s[0]=="0":#判断是不是16进制数
        if s[1]!="x" and s[1]!="X":
            for i in range(2,len(s)):
                if not s[i].isdigit():
                    return False
            return True
        elif s[1]=="x" or s[1]=="X":
            for i in range(2,len(s)):
                if not (s[i].isdigit() or (s[i] in ["a","b","c","d","e","f","A","B","C","D","E","F"])):
                    return False
            return True
    elif s.count(".")==1:#判断是否为浮点数
        tmpStr=s.split(".")
        leftStr=tmpStr[0]
        rightStr=tmpStr[1]
        if not leftStr.isdigit():
            return False
        if rightStr.isdigit():
            return True
        if not ("e" in rightStr) or (s.count("e")!=1):
            return False
        tmpStr=rightStr.split("e")
        leftStr=tmpStr[0]
        rightStr=tmpStr[1]
        if leftStr.isdigit() and rightStr.isdigit():
            return True
    elif s.count("e")==1:#判断是否为科学计数法
        tmpStr=s.split("e")
        leftStr=tmpStr[0]
        rightStr=tmpStr[1]
        if leftStr.isdigit() and rightStr.isdigit():
            return True
    elif s.isdigit():
        return True
    return False

def scanner(code:list[str]):
    token=[]
    error=[]
    for i in range(len(code)):#遍历源代码
        if len(code[i])==0:#空行
            continue
        j=0
        while j <len(code[i]):
            if code[i][j]=="_" or code[i][j].isalpha():#处理标识符和保留字
                tmp=code[i][j]
                j+=1
                while j<len(code[i]):
                    if not (code[i][j]=="_" or code[i][j].isalpha() or code[i][j].isdigit()):
                        j-=1
                        break
                    tmp+=code[i][j]
                    j+=1
                Type=isReserved(tmp)
                if Type == "id":#是标识符
                    token.append(("<"+Type+", "+tmp+">").center(20)+"\t"+"Line:"+str(i+1))
                else:#是保留字
                    token.append(("<reserved, "+Type+">").center(20)+"\t"+"Line:"+str(i+1))
            elif code[i][j].isdigit():#处理数字
                tmp=code[i][j]
                j+=1
                while j<len(code[i]):
                    if not (code[i][j] == '.' or code[i][j] == 'e' or code[i][j] == 'x' or code[i][j] == 'X' or code[i][j].isdigit() or code[i][j].isalpha()):
                        j-=1
                        break
                    tmp=tmp+code[i][j]
                    j+=1
                if isNum(tmp):
                    token.append(("<const, "+tmp+">").center(20)+"\t"+"Line:"+str(i+1))
                else:
                    error.append("Constant error at line "+str(i+1)+":"+tmp)
                    while len(tmp)!=0:
                        tmp=tmp[1:]
                        if isNum(tmp):
                            token.append(("<const, "+tmp+">").center(20)+"\t"+"Line:"+str(i+1))
                            break
            elif code[i][j] in delimiter:#处理界限符
                token.append(("<delimiter, "+code[i][j]+">").center(20)+"\t"+"Line:"+str(i+1))
            elif code[i][j]=='"':#处理字符串
                j+=1
                tmp=""
                while j<len(code[i]) and code[i][j]!='"':
                    tmp+=code[i][j]
                    j+=1
                token.append('<delimiter, ">'.center(20)+"\t"+"Line:"+str(i+1))
                token.append(("<"+"const"+", "+tmp+">").center(20)+"\t"+"Line:"+str(i+1))
                token.append('<delimiter, ">'.center(20)+"\t"+"Line:"+str(i+1))
            elif code[i][j] in Operations1:#判断==,!=,<=,>=双目运算符
                if j<len(code[i])-1 and code[i][j+1]=="=":
                    token.append(("<op, "+code[i][j]+code[i][j+1]+">").center(20)+"\t"+"Line:"+str(i+1))
                    j+=1
                else:
                    token.append(("<op, "+code[i][j]+">").center(20)+"\t"+"Line:"+str(i+1))
            elif code[i][j] in Operations2:# && || 运算符
                if j <len(code[i])-1 and code[i][j+1]==code[i][j]:
                    if code[i][j]=="|":
                        token.append("<op, ||>".center(20)+"\t"+"Line:"+str(i+1))
                    if code[i][j]=="&":
                        token.append("<op, &&>".center(20)+"\t"+"Line:"+str(i+1))
                    j+=1
                else:
                    token.append(("<op, "+code[i][j]+">").center(20)+"\t"+"Line:"+str(i+1))
            elif code[i][j]==" ":
                j+=1
                continue
            else:
                error.append("Character error at line "+str(i+1)+":"+code[i][j])
            j+=1
    return token,error

def main():
    code=[]
    with open(inputPath,"r") as file:
        for line in file:
            code.append(line.strip())
    token,error=scanner(code)
    with open(outputPath,"w") as file:
        for _ in token:
            file.write(_+"\n")
        if len(error)!=0:
            file.write("errors:"+"\n")
            for _ in error:
                file.write(_+"\n")
        else:
            file.write("no error!")

if __name__=="__main__":
    main()
    print("Scan successful!")
            